<?php
/**
 * User: zhangligang
 * Email: tzhang2450@163.com
 * Date: 2018/9/7
 * Time: 23:57
 */

namespace App\Providers;


class Xyk
{
    private $partner_id;
    private $sign_key;
    private $aes_key;
    private $sign_off = true;//是否验证返回数据的签名
    /**
     * 初始化配置，包括合作方id、微微校和合作方两对AES密钥、签名key
     * @param array $options
     * {
     * 		id:'xxx',
     * 		aes_key:'xxx',
     * 		sign_key:'xxx'
     * }
     */
    public function __construct($options)
    {
        $this->partner_id = isset($options['id'])?$options['id']:'';
        $this->aes_key = isset($options['aes_key'])?$options['aes_key']:'';
        $this->sign_key = isset($options['sign_key'])?$options['sign_key']:'';
        $this->sign_off = isset($options['sign_off'])?$options['sign_off']:true;
    }

    private function pkcs5_pad ($text, $blocksize) {
        $pad = $blocksize - (strlen($text) % $blocksize);
        return $text.str_repeat(chr($pad), $pad);
    }

    function pkcs5_unpad($text)
    {
        $pad = ord($text{strlen($text) - 1});
        if ($pad > strlen($text)) return false;
        if (strspn($text, chr($pad), strlen($text) - $pad) != $pad) return false;
        return substr($text, 0, -1 * $pad);
    }

    /**
     * AES加密字符串
     * @param string $str 待加密字符串
     * @return boolean|string 失败返回false，成功则返回加密并base64编码后的字符串
     */
    private function encrypt($str){
        $str = $this->pkcs5_pad($str, 16);

        $iv = substr($this->aes_key, 0, 16);

        $encrypted = openssl_encrypt($str, 'AES-256-CBC', $this->aes_key, OPENSSL_RAW_DATA|OPENSSL_ZERO_PADDING, $iv);

        return base64_encode($encrypted);
    }

    /**
     * AES私钥加密字符串
     * @param string $str 待解密字符串
     * @return boolean|string 失败返回false，成功返回解密后的字符串
     */
    private function decrypt($str){
        $str = base64_decode($str);
        $iv = substr($this->aes_key, 0, 16);
        $encrypted = openssl_decrypt($str, 'AES-256-CBC', $this->aes_key, OPENSSL_RAW_DATA|OPENSSL_ZERO_PADDING, $iv);

        return $this->pkcs5_unpad($encrypted);
    }

    /**
     * 生成请求参数的sign签名字段
     * @param array $data
     * { id:'xxx',param:'xxx'}
     * @return string
     */
    public function gen_data_sign($data){
        return md5($data['id'].$data['param'].$this->sign_key);
    }

    /**
     * 生成响应参数的sign签名字段
     * @param array $result
     * {ret_code:'0',ret_content:'xxx'}
     * @return string
     */
    public function gen_result_sign($result){
        return md5($result['ret_code'].$result['ret_content'].$this->sign_key);
    }

    /**
     * 向第三方接口发送http post请求
     * @param string $url 第三方接口url
     * @param array $param 请求参数
     * @return Ambigous <string, boolean, mixed>
     */
    public function post($url, $param){
        $data['id'] = $this->partner_id;
        if(!is_string($param)){
            $param = json_encode($param);
        }
        $data['param'] = $this->encrypt($param);
        $data['sign'] = $this->gen_data_sign($data);
        return $this->send_post($url, $data);
    }

    private function send_post($url, $post_data) {
        $postdata = http_build_query($post_data);
        $options = array(
            'http' => array(
                'method' => 'POST',
                'header' => 'Content-type:application/x-www-form-urlencoded',
                'content' => $postdata,
            )  );
        $context = stream_context_create($options);
        $result = file_get_contents($url, false, $context);
        return $result;}

    /**
     * http post请求参数验签
     * @param array $data
     * @param string $sign_key
     * @return boolean
     */
    public function verify_data($data,$sign_key){
        return $data['sign'] == md5($data['id'].$data['param'].$sign_key);
    }

    /**
     * http post响应结果验签
     * @param array $data
     * @param string $sign_key
     * @return boolean
     */
    public function verify_result($data,$sign_key){
        if ($this->sign_off) {
            return true;
        }
        return $data['sign'] == md5($data['ret_code'].$data['ret_content'].$sign_key);
    }

    /**
     * 验签并解密http post参数的param字段
     * @param unknown $data
     * @return boolean
     */
    public function decrypt_data(&$data){
        if (!$this->verify_data($data, $this->sign_key)) {
            return false;
        }
        $decrypted = $this->decrypt($data['param']);
        if(!$decrypted){
            return false;
        }
        $data['param'] = json_decode($decrypted,true);
        if(empty($data['param'])){
            $data['param'] = $decrypted;
        }
        return true;
    }

    /**
     * 验签并解密http post响应的ret_content字段
     * @param array $data
     * @return boolean
     */
    public function decrypt_result(&$data){
        if (!$this->verify_result($data, $this->sign_key)) {
            var_dump('sign error');
            return false;
        }
        $decrypted = $this->decrypt($data['ret_content']);
        if(!$decrypted){
            var_dump('decrypt error');
            return false;
        }
        if($data['ret_code'] == 0){
            $data['ret_content'] = json_decode($decrypted,true);
        }
        else{
            $data['ret_content'] = $decrypted;
        }
        if(empty($data['param'])){
            $data['param'] = $decrypted;
        }
        return true;
    }

    /**
     * 生成http post响应结果，加密&签名
     * @param unknown $ret_code
     * @param unknown $ret_content
     * @return string
     */
    public function gen_result($ret_code,$ret_content){
        $result['ret_code'] = $ret_code;
        if (!is_string($ret_content)) {
            $ret_content = json_encode($ret_content);
        }
        $result['ret_content'] = $this->encrypt($ret_content);
        $result['sign'] = $this->gen_result_sign($result);
        return $result;
    }

    /**
     * 生成http post响应结果，加密&签名
     * @param unknown $ret_code
     * @param unknown $ret_content
     * @return string
     */
    public function gen_json_result($ret_code,$ret_content){
        return json_encode($this->gen_result($ret_code, $ret_content));
    }
}